Repository navigation
Conversation
Quest-Claim: c04c502918737a36
quest setup installs a PreToolUse hook for Claude Code and Codex that denies tool calls breaking the rules a program can check: em dashes an agent adds, a git push without explicit refspecs, a force-push or rebase, a new GitHub post without its footer, and scratch writes under /tmp. Their prose leaves the hook's context and stays in `quest context` for agents without it. quest setup also writes Codex's SessionStart rules hook to hooks.json, with an additionalContextLimit that keeps the rules whole. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d shells, check only clone destinations Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…separate git dir Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Design change: Narrow the body sources. Drop the in-command file tracking (the (Written by Claude Opus 5.5) |
…rule-checks # Conflicts: # README.md # quest/m0/token-waste/README.md # src/cli/hooks.ts # src/cli/main.ts # src/runner/prompt.ts # test/hooks.test.ts
Drops the in-command file tracking that read a here-document's text as the body file's contents. A file an earlier command on the line writes now reads as unknown and passes; `--body-file -` with a here-document stays checked. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Warning Review limit reachedYou've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Next included review available in 42 minutes. View limit details
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. |
kixelated
left a comment
There was a problem hiding this comment.
Automated review by review (OpenAI)
[P2] Do not inspect relative body files against a stale working directory (src/cli/rules.ts:203). shell() keeps the hook's original cwd for every simple command and does not handle cd. For example, if body.md in the initial directory lacks a footer but .tmp/body.md contains a valid post with its footer, cd .tmp && gh pr comment 5 --body-file body.md reads the former and is denied even though gh would read the latter. The same issue affects stdin redirection. This contradicts the narrow, fail-open behavior: an unrelated file can block a legitimate post. Track directory changes where they are known, or treat relative body sources as unknown after a command changes the directory. Add a regression covering two same-named files with different contents and a preceding cd.
Direction: fix this false denial before landing; the chosen policy of letting unknown body sources pass does not require broader shell emulation.
Static review of the 12-file diff, tests, and recorded design decision only. No shell commands, tests, builds, or live hook checks were run; runtime behavior and CI success are not certified.
(Written by review (OpenAI))
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Design change: Check inline bodies only. Stop reading body files from disk: check only (Written by Claude Opus 5.5) |
The maintainer's pick at the second round-3 design checkpoint: a body file may hold something else by the time the line posts it, so the hook checks only --body, body= fields, git commit -m, and --body-file - with a here-document. The per-line cd and write tracking goes with it, and the footer and em-dash rules stay in prose for bodies posted from a file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…refspecs Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…target options Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Finishes quest/m0/token-waste/rule-checks.md: the rules in
assets/CONTEXT.mdthat a program can check are now enforced by aPreToolUsehook instead of read as prose, for Claude Code and Codex, andquest setupgives Codex the sameSessionStartrules hook Claude Code got in moq-dev/quest#517.Behavior
quest setupinstalls one more entry per agent, each runningquest hook:settings.json):PreToolUseonBash|Write|Edit|MultiEdit, next to the existingSessionStartandSubagentStart.$CODEX_HOME/hooks.json, written only when that directory exists):SessionStart(startup|resume|clear|compact) withadditionalContextLimit: 10000, andPreToolUseonBash|apply_patch.quest setupprints a reminder to trust them through/hooks.In a repository with the
Quests:line,quest hookdenies a tool call, giving the rule as the reason, when it:>quote lines;git pushwith a refspec lacking:(or none), outside--delete,--tags,--all,--branches,--mirror;-fin any cluster,--force,--force-with-lease,--mirror, a+refspec), or runsgit rebaseorgit pull --rebase, unless the command starts withQUEST_USER_ASKED=1;git rebase --abort/--quitpass;gh pr create|comment|review,gh issue create|comment,gh apiwith abodyfield, notPATCH/PUT) with a body lacking a(Written by ...)line;/tmpoutside the checkout: redirections,tee,mkdir,touch, the destination ofcp/mv/ln/rsync,git clone,git worktree add,mktempwithout a directory, andWrite/Edit/apply_patchpaths.A new
src/cli/shell.tssplits a command line into simple commands (quotes,&&/||/;/pipes/subshells, redirections, here-documents and here-strings), and the checks peelenv, assignments,command,timeout,direnv exec,nix develop --command,quest gate, andbash -cto reach the real command. The footer and em-dash checks read a body only from the command line itself:--body, a-f body=field,git commit -m, and--body-file -with a here-document. They never read a body file from disk.The checked prose left
assets/CONTEXT.md(9,997 to 9,535 characters).quest contextand the runner's prompt still print the rules, as a last## Checkedsection built from the same strings the hook denies with, so an agent without the hook, or a kix runner job, still reads them; only the hook's context leaves it out.What stayed prose, and why
/tmpis checked.--body-file path,--body-file - < path,-F body=@path,git commit -F path) or piped intogh: passes unread, since the file may hold something else by the time the line runs, and every review finding in six rounds came from guessing it.assets/CONTEXT.mdkeeps a line next to "Pass PR bodies toghon stdin" saying such a body goes unchecked and still needs the footer and no em dashes.Choices made without asking
QUEST_USER_ASKED=1when the user asked in their own words. Rejected: keeping them prose (the habitual mistake goes unchecked), and Claude Code'saskdecision (Codex does not support it and lets the call through, and it stalls unattended sessions).>quote lines keep quoted ones.gh pr edit,gh issue edit, andgh api -X PATCHare not checked for em dashes or the footer, since an existing body holds other authors' text, such as a reviewer's record.(Written by ...), case-insensitive, with only whitespace after it. A sectionquest reviewappends later never trips it, since that is an edit.quest hookalready adds the context: repositories with theQuests:line./tmppaths inside the checkout holding the cwd pass, so a checkout that lives under/tmpkeeps working. A$TMPDIRpath passes, sinceTMPDIRmay name the checkout (a kix runner job sets it to the job's checkout);mktempwithout a directory still counts as/tmpunless aTMPDIR=assignment in front of it says otherwise.git <sub> --help,mktemp --help/-u) pass, as do the read-only operands ofcp/mv/ln -t <dir>andtouch -r <file>.SubagentStartentry; the quest's Plan asked only forSessionStartparity.Co-Authored-By:trailer were exempt from the footer; the hook never checks commits for it, so that clause went with the rule.Codex evidence
additionalContextLimitis in tokens: a hook printing 10,000 characters ofword(about 2,000 tokens) spilled tohook_outputsat a limit of 1000, but not at 3000 or the default. The limit is set to the rules' 10,000-character cap, which bounds their tokens.quest reviewkeeps user hooks out: in this checkout,codex execran the user's trustedSessionStarthook (hook: SessionStartin its output, its.direnv/codex-env.shwritten), andcodex exec --ignore-user-configdid not (no output line, no file). With--dangerously-bypass-hook-trust,hooks.jsondoes load under--ignore-user-config, so what keeps hooks out of reviews is that their trust lives inconfig.toml's[hooks.state], which the flag skips. No change toquest reviewwas needed.Public API
quest setupalso writes$CODEX_HOME/hooks.json(or~/.codex/hooks.json) when that directory exists, and adds thePreToolUseentry to Claude Code's settings. Rerunning it replaces the earlier entries.quest hookhandlesPreToolUseinput from either agent and prints apermissionDecision: "deny"on a broken rule.quest contextends with a## Checkedsection.Validation
just checkandjust testpass (1228 + 562 tests).test/rules.test.ts(parser, each rule's blocked and passing cases) andtest/hooks.test.ts(Codex target, handler settings,PreToolUsedispatch)..claude/settings.local.json,git push --dry-run origin mainwas denied with the refspec rule. Aquest hookcall takes about 0.1s.Decisions
ghcheck finds a post's body; the open one read a body file rewritten by an expanding here-document from its stale contents on disk), settled by the maintainer on 2026-11-08 in moq-dev/quest#553 (comment): ✅ narrow the body sources: drop the in-command file tracking, so a body file the same command line writes (by redirection,tee, or as the destination ofcp/mv/ln/rsync) reads as unknown and passes, while--body-file -with a here-document stays checked directly (rejected: keep in-command tracking and mark expanded writes as unknown; merge as is; drop the footer and em-dash checks onghcommands).cdon the line, then inside a nested shell after one), settled by the maintainer on 2026-11-08 in moq-dev/quest#553 (comment): ✅ check inline bodies only: never read a body file from disk, check--body,-f body=,git commit -m, and--body-file -with a here-document, and keep the footer and em-dash rules in prose for a body posted from a file (rejected: read a body file only whenghorgit commit -Fis alone on the line; keep theghpost checks as prose entirely; drop the quest). Reason: the quest's Plan says a check that cannot stay narrow stays prose. The per-linecdand write tracking went with it.git push --tags origin featurenow needs its refspec's:(only a tags-only push is exempt);mktemphonors aTMPDIR=assignment in front of it; (moq-dev/quest#553 review). Refuted: keeping the checked prose in the hook's context, since the quest's Goal removes it.quest setup --worktrees: the worktree hooks go only into Claude Code's settings;HookEntrycarries acommandbeside its handlersettings, which hold theSessionEndtimeout.Waiting
mktempmodes,$TMPDIR, andgit push --repo(all fixed), and the three still open: wherecp/mv/ln/rsync/touchput their destination in the/tmpcheck (cp -t <dir>,cp -t.tmp,rsync -t,touch -r/--reference,ln -s <target>with an implicit destination), and wheregitaccepts a help flag (git rebase --interactive -h). Each fix exposes the next variant. Options:cp/mv/ln/rsync/touchfrom the/tmpcheck, keeping redirections,tee,mkdir,mktemp,git clone/worktree add, andWrite/Edit/apply_patchpaths, where the habitual mistake (logs and bodies written to/tmp) happens. Pass anygitcommand with-h/--helpanywhere in it, accepting the rare false pass ofgit push -o --help, since a missed nudge is not a block. This removes the class of findings and some code, following the quest's rule that a check which cannot stay narrow stays prose.-t<dir>,ln's one-operand form, and help flags after other options, keeping per-command grammars./tmpcheck further, to redirections andWrite/Edit/apply_patchpaths only.Check: a
Design change:PR comment records the pick, then/quest-iterateapplies it and reviews withquest review --new.Models
Size: M
Implementer: Claude Code, claude-opus-5-5, medium effort
Implementer: Claude Code, claude-opus-5-5, low effort (iterate, M)
Suggested follow-ups
None worth a quest.
(Written by Claude Opus 5.5)
Review
Rewritten by
quest reviewafter each round.01a1b9c4-426c-7703-878b-1c76630de27c, round 3All four reported cases are fixed, but destination and help-option parsing still rejects several compliant commands.src/cli/rules.ts:208:[P2] Recognize attached target-directory argumentssrc/cli/rules.ts:209:[P2] Handle ln's implicit destinationsrc/cli/rules.ts:249:[P2] Recognize actual help flags after other optionssrc/cli/rules.ts:204:[P2] Recognize long reference options before classifying operands;The valued-option set at src/cli/rules.ts:204 now includes --reference, and the previously denied reference-input example passes.src/cli/rules.ts:207:[P2] Keep rsync option semantics separate from cp;The command-specific branch at src/cli/rules.ts:208 excludes rsync from target-directory parsing, correctly handling both reproduced rsync destinations.src/cli/rules.ts:248:[P2] Distinguish help flags from option values;The narrowed help check at src/cli/rules.ts:249 no longer exempts `git push -o --help origin main`, which is correctly denied.src/cli/rules.ts:406:[P3] Anchor attribution to the complete body's end;The regex at src/cli/rules.ts:407 rejects content after attribution while allowing a final attribution line followed by whitespace.